﻿#include "pch.h"
#include "qgp/qgach.h"

//////////////////////////////////////////////////////////////////////////
// 스터브
KIMPL_SUBCLASS_ABSTRACT(qgStub, "RenderStub");

namespace _qgp
{
	static char StubClassName[64] = "__qg_stub_class__";

	extern qgStub* NewStub(const tpl::Rect& area, kint flags, const char* title, kpointer app);
	extern qgStub* NewStubVirtual(kpointer handle);
}

qgStub* qgStub::Instance = NULL;

qgStub::qgStub()
	: _lock()
	, _delay(10)
	, _btitle("QG")
{
	_timer = k_timer_new();

#if !_SB_WINDOWS_ || TRUE
	k_timer_set_manual(_timer, true);	// 유닉스에서 문제가 생기는 것을 막기 위함
#endif

	// 마우스 더블크릭
	_mouse.mclick.limmove = 5 * 5;		// 제한 이동 거리 제곱
	_mouse.mclick.limtick = 500;		// 제한 클릭 시간

	//
	if (Instance == NULL)
		Instance = this;
}

qgStub::~qgStub()
{
	ksize_t i;

	for (i = 0; i < QGFSCL_MAX_VALUE; i++)
	{
		kForEach(qbFsReader* l, _fsrd[i])
			l->Dispose();

		kForEach(qbFsWriter* l, _fswt[i])
			l->Dispose();
	}

	kobj_unload(_mount);

	k_timer_delete(_timer);

	//
	if (Instance == this)
		Instance = NULL;
}

//
bool qgStub::_Close()
{
	return true;
}

//
kbool qgStub::Poll(kpointer osstate)
{
	return true;
}

//
kbool qgStub::Layout()
{
	return true;
}

//
kbool qgStub::Run(kpointer osstate)
{
	if (!K_OFLAG(_insts, QGSDI_VIRTUAL))
	{
		if (!Poll(osstate) || K_OFLAG(_insts, QGSDI_EXIT))
		{
			// 스터브의 실행이 끝남
			return false;
		}
	}

	k_timer_update(_timer);

	float adv = (float)k_timer_get_adv(_timer);

	_runfps = (float)k_timer_get_fps(_timer);
	_runtime = (float)k_timer_get_run(_timer);
	_advance[0] = adv;
	_advance[2] = adv;

	if (!K_OFLAG(_insts, QGSDI_PAUSE))
	{
		_advance[1] = adv;
		_advance[3] = adv;
	}

	if (K_OFLAG(_insts, QGSDI_GOPU))
	{
		k_sleep(K_OFLAG(_flags, QGSDF_LATENCY) || !K_OFLAG(_insts, QGSDI_ACTIVE | QGSDI_VIRTUAL) ? _delay : 1);
		//k_mesg("GOPU SLEEP!\n");
	}
	else if (K_OFLAG(_flags, QGSDF_LATENCY) || !K_OFLAG(_insts, QGSDI_ACTIVE | QGSDI_VIRTUAL))
	{
		k_sleep(_delay);
		//k_mesg("LATENCY SLEEP!\n");
	}

	return true;
}

//
kbool qgStub::GetMouse(qgUimMouse* v)
{
	k_return_value_if_fail(v, false);

	*v = _mouse;

	return true;
}

//
kbool qgStub::GetKey(qgUimKey* k)
{
	k_return_value_if_fail(k, false);

	*k = _key;

	return true;
}

//
kbool qgStub::IsKeyDown(kbyte key)
{
	return _key.key[key] != 0;
}

//
kbool qgStub::GetControllerInput(kint /*slot*/, qgUimControllerInput* /*inf*/)
{
	return false;
}

//
kbool qgStub::GetControllerInfo(kint /*slot*/, qgUimControllerInfo* /*stt*/)
{
	return false;
}

//
kvoid qgStub::SetKeyDown(kbyte key, kbool down)
{
	_key.key[key] = down;
}

//
kbool qgStub::SetControllerEnable(kbool /*v*/)
{
	return false;
}

//
kbool qgStub::SetControllerVibration(kint /*slot*/, const qgUimControllerVibration& /*v*/)
{
	return false;
}

//
kvoid qgStub::SetDropEnable(kbool /*v*/)
{

}

//
kvoid qgStub::SetAcsEnable(kbool /*v*/)
{

}

//
kvoid qgStub::SetImeEnable(kbool v)
{
}

//
kbool qgStub::SetTitle(const char* text)
{
	kGuardSpinLock(_lock)
		_btitle = text;

	return true;
}

//
kvoid qgStub::SetLimitSize(kint width, kint height)
{
	kGuardSpinLock(_lock)
		_limit.Set(K_MAX(width, 0), K_MAX(height, 0));
}

//
kvoid qgStub::HoldMouse(kbool /*v*/)
{

}

//
void qgStub::AddHandler(qbSevHandler* h)
{
	k_return_if_fail(h != NULL);

	kGuardSpinLock(_lock)
		_sehs.push_back(h);
}

//
bool qgStub::RemoveHandler(qbSevHandler* h)
{
	k_return_value_if_fail(h != NULL, false);

	bool ret = false;

	kGuardSpinLock(_lock)
	{
		auto it = std::find(_sehs.begin(), _sehs.end(), h);
		if (it != _sehs.end())
		{
			_sehs.erase(it);
			ret = true;
		}
	}

	return ret;
}

//
kvoid qgStub::RegisterReader(qgFsClass slt, qbFsReader* ptr)
{
	k_return_if_fail((ksize_t)slt < QGFSCL_MAX_VALUE);
	k_return_if_fail(ptr != NULL);

	kGuardSpinLock(_lock)
		_fsrd[slt].push_back(ptr);
}

//
kbool qgStub::PerformRead(kObj* ctnr, kconstpointer data, kint size, kint flags, qgFsClass slt /*= QGFSCL_FORCE*/)
{
	kGuardSpinLock(_lock)
	{
		if (slt == QGFSCL_FORCE)
		{
			for (ksize_t i = 0; i < QGFSCL_MAX_VALUE; i++)
			{
				kForEach(qbFsReader* l, _fsrd[i])
				{
					if (l->Test(data, size) && l->Read(ctnr, data, size, flags))
						return true;
				}
			}
		}
		else if ((ksize_t)slt < QGFSCL_MAX_VALUE)
		{
			kForEach(qbFsReader* l, _fsrd[slt])
			{
				if (l->Test(data, size) && l->Read(ctnr, data, size, flags))
					return true;
			}
		}
	}

	return false;
}

//
kvoid qgStub::RegisterWriter(qgFsClass slt, qbFsWriter* ptr)
{
	k_return_if_fail((ksize_t)slt < QGFSCL_MAX_VALUE);
	k_return_if_fail(ptr != NULL);

	kGuardSpinLock(_lock)
		_fswt[slt].push_back(ptr);
}

//
kbool qgStub::PerformWrite(kStream* st, kObj* ctnr, kint flags, qgFsClass slt /* = QGFSCL_FORCE */)
{
	const char* desc = st->GetDescPtr();
	k_return_value_if_fail(desc, false);

	char* psz = k_strrchr(desc, '.');
	k_return_value_if_fail(psz, false);

	const char* ext = psz + 1;

	kGuardSpinLock(_lock)
	{
		if (slt == QGFSCL_FORCE)
		{
			for (ksize_t i = 0; i < QGFSCL_MAX_VALUE; i++)
			{
				kForEach(qbFsWriter* l, _fswt[i])
				{
					if (l->Test(ext) && l->Write(st, ctnr, flags))
						return true;
				}
			}
		}
		else if ((ksize_t)slt < QGFSCL_MAX_VALUE)
		{
			kForEach(qbFsWriter* l, _fswt[slt])
			{
				if (l->Test(ext) && l->Write(st, ctnr, flags))
					return true;
			}
		}
	}

	return false;
}

//
kint qgStub::Mount(const char* path, bool autoarchive)
{
	kint ret = -1;

	kGuardSpinLock(_lock)
	{
		kobj_reset(&_mount);

		char* ext = path ? k_strrchr(path, '.') : NULL;

		if (!ext)
		{
			_mount = kFuse::New(path);

			if (_mount)
			{
				if (!autoarchive)
					ret = 0;
				else
				{
					// 오토 마운트는 해당 경로의 모든 hfs를 가져오는 것임
					ret = ((kFuse*)_mount)->AutoMount();
				}
			}
		}
		else
		{
			const char* psz = ext + 1;

			if (k_strieqlv(psz, "hfs"))
				_mount = kHfs::New(path);
			else
			{
				// 헐...
			}

			if (_mount)
				ret = 0;
		}
	}

	return ret;
}

//
bool qgStub::_ProcessMultiClick(kint button, kint method)
{
	if (method == QIMT_MOVE)
	{
		if (_mouse.mclick.tick > 0)
		{
			kint dx = _mouse.mclick.location.x - _mouse.point.x;
			kint dy = _mouse.mclick.location.y - _mouse.point.y;
			kint d = dx * dx + dy * dy;

			if (d > _mouse.mclick.limmove)
			{
				// 마우스가 _mouse.mclick.limmove 포인트 움직이면 두번 누르기 취소
				_mouse.mclick.tick = 0;
			}
		}
	}
	else if (method == QIMT_DOWN)
	{
		if (_mouse.mclick.tick == 0)
		{
			_mouse.mclick.tick = k_tick();
			_mouse.mclick.location = _mouse.point;
			_mouse.mclick.button = button;
		}
		else
		{
			if (_mouse.mclick.button == button)
			{
				kuint delta = k_tick() - _mouse.mclick.tick;

				if (delta < _mouse.mclick.limtick)
				{
					// 더블클릭했음
					_mouse.mclick.tick = 0;
					return true;
				}
			}

			// 취소
			_mouse.mclick.tick = 0;
		}
	}
	else if (method == QIMT_UP)
	{
		// 할거 없네...
	}

	return false;
}

//
const char* qgStub::GetInternalClassName()
{
	return _qgp::StubClassName;
}

//
kvoid qgStub::SetInternalClassName(const char* name)
{
	if (name)
		k_strncpy(_qgp::StubClassName, name, 63);
}

//
qgStub* qgStub::New(const kRt& bound, const char* text, kpointer app, kint flags)
{
	qgStub* stub = _qgp::NewStub(bound, flags, text, app);

	return stub;
}

//
qgStub* qgStub::New(kint width, kint height, const char* text, kpointer app, kint flags)
{
	qgStub* stub = _qgp::NewStub(tpl::Rect(0, 0, width, height), flags, text, app);

	return stub;
}

//
qgStub* qgStub::NewVirtual(kpointer handle)
{
	qgStub* stub = _qgp::NewStubVirtual(handle);

	return stub;
}
